home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / WarpQuake / Src / host.c < prev    next >
C/C++ Source or Header  |  2000-05-22  |  20KB  |  965 lines

  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // host.c -- coordinates spawning and killing of local servers
  21.  
  22. #include "quakedef.h"
  23. #include "r_local.h"
  24.  
  25. /*
  26.  
  27. A server can allways be started, even if the system started out as a client
  28. to a remote system.
  29.  
  30. A client can NOT be started if the system started as a dedicated server.
  31.  
  32. Memory is cleared / released when a server or client begins, not when they end.
  33.  
  34. */
  35.  
  36. quakeparms_t host_parms;
  37.  
  38. qboolean    host_initialized;        // true if into command execution
  39.  
  40. double        host_frametime;
  41. double        host_time;
  42. double        realtime;                // without any filtering or bounding
  43. double        oldrealtime;            // last frame run
  44. int            host_framecount;
  45.  
  46. int            host_hunklevel;
  47.  
  48. int            minimum_memory;
  49.  
  50. client_t    *host_client;            // current client
  51.  
  52. jmp_buf     host_abortserver;
  53.  
  54. byte        *host_basepal;
  55. byte        *host_colormap;
  56.  
  57. cvar_t    host_framerate = {"host_framerate","0"};    // set for slow motion
  58. cvar_t    host_speeds = {"host_speeds","0"};            // set for running times
  59.  
  60. cvar_t    sys_ticrate = {"sys_ticrate","0.05"};
  61. cvar_t    serverprofile = {"serverprofile","0"};
  62.  
  63. cvar_t    fraglimit = {"fraglimit","0",false,true};
  64. cvar_t    timelimit = {"timelimit","0",false,true};
  65. cvar_t    teamplay = {"teamplay","0",false,true};
  66.  
  67. cvar_t    samelevel = {"samelevel","0"};
  68. cvar_t    noexit = {"noexit","0",false,true};
  69.  
  70. #ifdef QUAKE2
  71. cvar_t    developer = {"developer","1"};    // should be 0 for release!
  72. #else
  73. cvar_t    developer = {"developer","0"};
  74. #endif
  75.  
  76. cvar_t    skill = {"skill","1"};                        // 0 - 3
  77. cvar_t    deathmatch = {"deathmatch","0"};            // 0, 1, or 2
  78. cvar_t    coop = {"coop","0"};            // 0 or 1
  79.  
  80. cvar_t    pausable = {"pausable","1"};
  81.  
  82. cvar_t    temp1 = {"temp1","0"};
  83.  
  84. /*
  85. ================
  86. Host_EndGame
  87. ================
  88. */
  89. void Host_EndGame (char *message, ...)
  90. {
  91.     va_list        argptr;
  92.     char        string[1024];
  93.     
  94.     va_start (argptr,message);
  95.     vsprintf (string,message,argptr);
  96.     va_end (argptr);
  97.     Con_DPrintf ("Host_EndGame: %s\n",string);
  98.     
  99.     if (sv.active)
  100.         Host_ShutdownServer (false);
  101.  
  102.     if (cls.state == ca_dedicated)
  103.         Sys_Error ("Host_EndGame: %s\n",string);    // dedicated servers exit
  104.     
  105.     if (cls.demonum != -1)
  106.         CL_NextDemo ();
  107.     else
  108.         CL_Disconnect ();
  109.  
  110.     longjmp (host_abortserver, 1);
  111. }
  112.  
  113. /*
  114. ================
  115. Host_Error
  116.  
  117. This shuts down both the client and server
  118. ================
  119. */
  120. void Host_Error (char *error, ...)
  121. {
  122.     va_list        argptr;
  123.     char        string[1024];
  124.     static    qboolean inerror = false;
  125.     
  126.     if (inerror)
  127.         Sys_Error ("Host_Error: recursively entered");
  128.     inerror = true;
  129.     
  130.     SCR_EndLoadingPlaque ();        // reenable screen updates
  131.  
  132.     va_start (argptr,error);
  133.     vsprintf (string,error,argptr);
  134.     va_end (argptr);
  135.     Con_Printf ("Host_Error: %s\n",string);
  136.     
  137.     if (sv.active)
  138.         Host_ShutdownServer (false);
  139.  
  140.     if (cls.state == ca_dedicated)
  141.         Sys_Error ("Host_Error: %s\n",string);    // dedicated servers exit
  142.  
  143.     CL_Disconnect ();
  144.     cls.demonum = -1;
  145.  
  146.     inerror = false;
  147.  
  148.     longjmp (host_abortserver, 1);
  149. }
  150.  
  151. /*
  152. ================
  153. Host_FindMaxClients
  154. ================
  155. */
  156. void    Host_FindMaxClients (void)
  157. {
  158.     int        i;
  159.  
  160.     svs.maxclients = 1;
  161.         
  162.     i = COM_CheckParm ("-dedicated");
  163.     if (i)
  164.     {
  165.         cls.state = ca_dedicated;
  166.         if (i != (com_argc - 1))
  167.         {
  168.             svs.maxclients = Q_atoi (com_argv[i+1]);
  169.         }
  170.         else
  171.             svs.maxclients = 8;
  172.     }
  173.     else
  174.         cls.state = ca_disconnected;
  175.  
  176.     i = COM_CheckParm ("-listen");
  177.     if (i)
  178.     {
  179.         if (cls.state == ca_dedicated)
  180.             Sys_Error ("Only one of -dedicated or -listen can be specified");
  181.         if (i != (com_argc - 1))
  182.             svs.maxclients = Q_atoi (com_argv[i+1]);
  183.         else
  184.             svs.maxclients = 8;
  185.     }
  186.     if (svs.maxclients < 1)
  187.         svs.maxclients = 8;
  188.     else if (svs.maxclients > MAX_SCOREBOARD)
  189.         svs.maxclients = MAX_SCOREBOARD;
  190.  
  191.     svs.maxclientslimit = svs.maxclients;
  192.     if (svs.maxclientslimit < 4)
  193.         svs.maxclientslimit = 4;
  194.     svs.clients = Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
  195.  
  196.     if (svs.maxclients > 1)
  197.         Cvar_SetValue ("deathmatch", 1.0);
  198.     else
  199.         Cvar_SetValue ("deathmatch", 0.0);
  200. }
  201.  
  202.  
  203. /*
  204. =======================
  205. Host_InitLocal
  206. ======================
  207. */
  208. void Host_InitLocal (void)
  209. {
  210.     Host_InitCommands ();
  211.     
  212.     Cvar_RegisterVariable (&host_framerate);
  213.     Cvar_RegisterVariable (&host_speeds);
  214.  
  215.     Cvar_RegisterVariable (&sys_ticrate);
  216.     Cvar_RegisterVariable (&serverprofile);
  217.  
  218.     Cvar_RegisterVariable (&fraglimit);
  219.     Cvar_RegisterVariable (&timelimit);
  220.     Cvar_RegisterVariable (&teamplay);
  221.     Cvar_RegisterVariable (&samelevel);
  222.     Cvar_RegisterVariable (&noexit);
  223.     Cvar_RegisterVariable (&skill);
  224.     Cvar_RegisterVariable (&developer);
  225.     Cvar_RegisterVariable (&deathmatch);
  226.     Cvar_RegisterVariable (&coop);
  227.  
  228.     Cvar_RegisterVariable (&pausable);
  229.  
  230.     Cvar_RegisterVariable (&temp1);
  231.  
  232.     Host_FindMaxClients ();
  233.     
  234.     host_time = 1.0;        // so a think at time 0 won't get called
  235. }
  236.  
  237.  
  238. /*
  239. ===============
  240. Host_WriteConfiguration
  241.  
  242. Writes key bindings and archived cvars to config.cfg
  243. ===============
  244. */
  245. void Host_WriteConfiguration (void)
  246. {
  247.     FILE    *f;
  248.  
  249. // dedicated servers initialize the host but don't parse and set the
  250. // config.cfg cvars
  251.     if (host_initialized & !isDedicated)
  252.     {
  253. #ifdef AMIGA
  254.         if (strlen(com_gamedir) == 0 ||
  255.             com_gamedir[strlen(com_gamedir)-1] == ':')
  256.             f = fopen (va("%sconfig.cfg",com_gamedir), "w");
  257.         else
  258.             f = fopen (va("%s/config.cfg",com_gamedir), "w");
  259. #else
  260.         f = fopen (va("%s/config.cfg",com_gamedir), "w");
  261. #endif
  262.         if (!f)
  263.         {
  264.             Con_Printf ("Couldn't write config.cfg.\n");
  265.             return;
  266.         }
  267.         
  268.         Key_WriteBindings (f);
  269.         Cvar_WriteVariables (f);
  270.  
  271.         fclose (f);
  272.     }
  273. }
  274.  
  275.  
  276. /*
  277. =================
  278. SV_ClientPrintf
  279.  
  280. Sends text across to be displayed 
  281. FIXME: make this just a stuffed echo?
  282. =================
  283. */
  284. void SV_ClientPrintf (char *fmt, ...)
  285. {
  286.     va_list        argptr;
  287.     char        string[1024];
  288.     
  289.     va_start (argptr,fmt);
  290.     vsprintf (string, fmt,argptr);
  291.     va_end (argptr);
  292.     
  293.     MSG_WriteByte (&host_client->message, svc_print);
  294.     MSG_WriteString (&host_client->message, string);
  295. }
  296.  
  297. /*
  298. =================
  299. SV_BroadcastPrintf
  300.  
  301. Sends text to all active clients
  302. =================
  303. */
  304. void SV_BroadcastPrintf (char *fmt, ...)
  305. {
  306.     va_list        argptr;
  307.     char        string[1024];
  308.     int            i;
  309.     
  310.     va_start (argptr,fmt);
  311.     vsprintf (string, fmt,argptr);
  312.     va_end (argptr);
  313.     
  314.     for (i=0 ; i<svs.maxclients ; i++)
  315.         if (svs.clients[i].active && svs.clients[i].spawned)
  316.         {
  317.             MSG_WriteByte (&svs.clients[i].message, svc_print);
  318.             MSG_WriteString (&svs.clients[i].message, string);
  319.         }
  320. }
  321.  
  322. /*
  323. =================
  324. Host_ClientCommands
  325.  
  326. Send text over to the client to be executed
  327. =================
  328. */
  329. void Host_ClientCommands (char *fmt, ...)
  330. {
  331.     va_list        argptr;
  332.     char        string[1024];
  333.     
  334.     va_start (argptr,fmt);
  335.     vsprintf (string, fmt,argptr);
  336.     va_end (argptr);
  337.     
  338.     MSG_WriteByte (&host_client->message, svc_stufftext);
  339.     MSG_WriteString (&host_client->message, string);
  340. }
  341.  
  342. /*
  343. =====================
  344. SV_DropClient
  345.  
  346. Called when the player is getting totally kicked off the host
  347. if (crash = true), don't bother sending signofs
  348. =====================
  349. */
  350. void SV_DropClient (qboolean crash)
  351. {
  352.     int        saveSelf;
  353.     int        i;
  354.     client_t *client;
  355.  
  356.     if (!crash)
  357.     {
  358.         // send any final messages (don't check for errors)
  359.         if (NET_CanSendMessage (host_client->netconnection))
  360.         {
  361.             MSG_WriteByte (&host_client->message, svc_disconnect);
  362.             NET_SendMessage (host_client->netconnection, &host_client->message);
  363.         }
  364.     
  365.         if (host_client->edict && host_client->spawned)
  366.         {
  367.         // call the prog function for removing a client
  368.         // this will set the body to a dead frame, among other things
  369.             saveSelf = pr_global_struct->self;
  370.             pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
  371.             PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
  372.             pr_global_struct->self = saveSelf;
  373.         }
  374.  
  375.         Sys_Printf ("Client %s removed\n",host_client->name);
  376.     }
  377.  
  378. // break the net connection
  379.     NET_Close (host_client->netconnection);
  380.     host_client->netconnection = NULL;
  381.  
  382. // free the client (the body stays around)
  383.     host_client->active = false;
  384.     host_client->name[0] = 0;
  385.     host_client->old_frags = -999999;
  386.     net_activeconnections--;
  387.  
  388. // send notification to all clients
  389.     for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
  390.     {
  391.         if (!client->active)
  392.             continue;
  393.         MSG_WriteByte (&client->message, svc_updatename);
  394.         MSG_WriteByte (&client->message, host_client - svs.clients);
  395.         MSG_WriteString (&client->message, "");
  396.         MSG_WriteByte (&client->message, svc_updatefrags);
  397.         MSG_WriteByte (&client->message, host_client - svs.clients);
  398.         MSG_WriteShort (&client->message, 0);
  399.         MSG_WriteByte (&client->message, svc_updatecolors);
  400.         MSG_WriteByte (&client->message, host_client - svs.clients);
  401.         MSG_WriteByte (&client->message, 0);
  402.     }
  403. }
  404.  
  405. /*
  406. ==================
  407. Host_ShutdownServer
  408.  
  409. This only happens at the end of a game, not between levels
  410. ==================
  411. */
  412. void Host_ShutdownServer(qboolean crash)
  413. {
  414.     int        i;
  415.     int        count;
  416.     sizebuf_t    buf;
  417.     char        message[4];
  418.     double    start;
  419.  
  420.     if (!sv.active)
  421.         return;
  422.  
  423.     sv.active = false;
  424.  
  425. // stop all client sounds immediately
  426.     if (cls.state == ca_connected)
  427.         CL_Disconnect ();
  428.  
  429. // flush any pending messages - like the score!!!
  430.     start = Sys_FloatTime();
  431.     do
  432.     {
  433.         count = 0;
  434.         for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
  435.         {
  436.             if (host_client->active && host_client->message.cursize)
  437.             {
  438.                 if (NET_CanSendMessage (host_client->netconnection))
  439.                 {
  440.                     NET_SendMessage(host_client->netconnection, &host_client->message);
  441.                     SZ_Clear (&host_client->message);
  442.                 }
  443.                 else
  444.                 {
  445.                     NET_GetMessage(host_client->netconnection);
  446.                     count++;
  447.                 }
  448.             }
  449.         }
  450.         if ((Sys_FloatTime() - start) > 3.0)
  451.             break;
  452.     }
  453.     while (count);
  454.  
  455. // make sure all the clients know we're disconnecting
  456.     buf.data = message;
  457.     buf.maxsize = 4;
  458.     buf.cursize = 0;
  459.     MSG_WriteByte(&buf, svc_disconnect);
  460.     count = NET_SendToAll(&buf, 5);
  461.     if (count)
  462.         Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
  463.  
  464.     for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
  465.         if (host_client->active)
  466.             SV_DropClient(crash);
  467.  
  468. //
  469. // clear structures
  470. //
  471.     memset (&sv, 0, sizeof(sv));
  472.     memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
  473. }
  474.  
  475.  
  476. /*
  477. ================
  478. Host_ClearMemory
  479.  
  480. This clears all the memory used by both the client and server, but does
  481. not reinitialize anything.
  482. ================
  483. */
  484. void Host_ClearMemory (void)
  485. {
  486.     Con_DPrintf ("Clearing memory\n");
  487.     D_FlushCaches ();
  488.     Mod_ClearAll ();
  489.     if (host_hunklevel)
  490.         Hunk_FreeToLowMark (host_hunklevel);
  491.  
  492.     cls.signon = 0;
  493.     memset (&sv, 0, sizeof(sv));
  494.     memset (&cl, 0, sizeof(cl));
  495. }
  496.  
  497.  
  498. //============================================================================
  499.  
  500.  
  501. /*
  502. ===================
  503. Host_FilterTime
  504.  
  505. Returns false if the time is too short to run a frame
  506. ===================
  507. */
  508. qboolean Host_FilterTime (float time)
  509. {
  510.     realtime += time;
  511.  
  512.     if ((!cls.timedemo || !cls.timedemo2) && (realtime - oldrealtime < 1.0/72.0))
  513.         return false;        // framerate is too high
  514.  
  515.     host_frametime = realtime - oldrealtime;
  516.     oldrealtime = realtime;
  517.  
  518.     if (host_framerate.value > 0)
  519.         host_frametime = host_framerate.value;
  520.     else
  521.     {    // don't allow really long or short frames
  522.         if (host_frametime > 0.1)
  523.             host_frametime = 0.1;
  524.         if (host_frametime < 0.001)
  525.             host_frametime = 0.001;
  526.     }
  527.     
  528.     return true;
  529. }
  530.  
  531.  
  532. /*
  533. ===================
  534. Host_GetConsoleCommands
  535.  
  536. Add them exactly as if they had been typed at the console
  537. ===================
  538. */
  539. void Host_GetConsoleCommands (void)
  540. {
  541.     char    *cmd;
  542.  
  543.     while (1)
  544.     {
  545.         cmd = Sys_ConsoleInput ();
  546.         if (!cmd)
  547.             break;
  548.         Cbuf_AddText (cmd);
  549.     }
  550. }
  551.  
  552.  
  553. /*
  554. ==================
  555. Host_ServerFrame
  556.  
  557. ==================
  558. */
  559. #ifdef FPS_20
  560.  
  561. void _Host_ServerFrame (void)
  562. {
  563. // run the world state    
  564.     pr_global_struct->frametime = host_frametime;
  565.  
  566. // read client messages
  567.     SV_RunClients ();
  568.     
  569. // move things around and think
  570. // always pause in single player if in console or menus
  571.     if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
  572.         SV_Physics ();
  573. }
  574.  
  575. void Host_ServerFrame (void)
  576. {
  577.     float    save_host_frametime;
  578.     float    temp_host_frametime;
  579.  
  580. // run the world state    
  581.     pr_global_struct->frametime = host_frametime;
  582.  
  583. // set the time and clear the general datagram
  584.     SV_ClearDatagram ();
  585.     
  586. // check for new clients
  587.     SV_CheckForNewClients ();
  588.  
  589.     temp_host_frametime = save_host_frametime = host_frametime;
  590.     while(temp_host_frametime > (1.0/72.0))
  591.     {
  592.         if (temp_host_frametime > 0.05)
  593.             host_frametime = 0.05;
  594.         else
  595.             host_frametime = temp_host_frametime;
  596.         temp_host_frametime -= host_frametime;
  597.         _Host_ServerFrame ();
  598.     }
  599.     host_frametime = save_host_frametime;
  600.  
  601. // send all messages to the clients
  602.     SV_SendClientMessages ();
  603. }
  604.  
  605. #else
  606.  
  607. void Host_ServerFrame (void)
  608. {
  609. // run the world state    
  610.     pr_global_struct->frametime = host_frametime;
  611.  
  612. // set the time and clear the general datagram
  613.     SV_ClearDatagram ();
  614.     
  615. // check for new clients
  616.     SV_CheckForNewClients ();
  617.  
  618. // read client messages
  619.     SV_RunClients ();
  620.     
  621. // move things around and think
  622. // always pause in single player if in console or menus
  623.     if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
  624.         SV_Physics ();
  625.  
  626. // send all messages to the clients
  627.     SV_SendClientMessages ();
  628. }
  629.  
  630. #endif
  631.  
  632.  
  633. /*
  634. ==================
  635. Host_Frame
  636.  
  637. Runs all active servers
  638. ==================
  639. */
  640. void _Host_Frame (float time)
  641. {
  642.     static double        time1 = 0;
  643.     static double        time2 = 0;
  644.     static double        time3 = 0;
  645.     int            pass1, pass2, pass3;
  646.  
  647.     if (setjmp (host_abortserver) )
  648.         return;            // something bad happened, or the server disconnected
  649.  
  650. // keep the random time dependent
  651.     rand ();
  652.     
  653. // decide the simulation time
  654.     if (!Host_FilterTime (time))
  655.         return;            // don't run too fast, or packets will flood out
  656.         
  657. // get new key events
  658.     Sys_SendKeyEvents ();
  659.  
  660. // allow mice or other external controllers to add commands
  661.     IN_Commands ();
  662.  
  663. // process console commands
  664.     Cbuf_Execute ();
  665.  
  666.     NET_Poll();
  667.  
  668. // if running the server locally, make intentions now
  669.     if (sv.active)
  670.         CL_SendCmd ();
  671.     
  672. //-------------------
  673. //
  674. // server operations
  675. //
  676. //-------------------
  677.  
  678. // check for commands typed to the host
  679.     Host_GetConsoleCommands ();
  680.     
  681.     if (sv.active)
  682.         Host_ServerFrame ();
  683.  
  684. //-------------------
  685. //
  686. // client operations
  687. //
  688. //-------------------
  689.  
  690. // if running the server remotely, send intentions now after
  691. // the incoming messages have been read
  692.     if (!sv.active)
  693.         CL_SendCmd ();
  694.  
  695.     host_time += host_frametime;
  696.  
  697. // fetch results from server
  698.     if (cls.state == ca_connected)
  699.     {
  700.         CL_ReadFromServer ();
  701.     }
  702.  
  703. // update video
  704.     if (host_speeds.value)
  705.         time1 = Sys_FloatTime ();
  706.         
  707.     SCR_UpdateScreen ();
  708.  
  709.     if (host_speeds.value)
  710.         time2 = Sys_FloatTime ();
  711.         
  712. // update audio
  713.     if (cls.signon == SIGNONS)
  714.     {
  715.         S_Update (r_origin, vpn, vright, vup);
  716.         CL_DecayLights ();
  717.     }
  718.     else
  719.         S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
  720.     
  721.     CDAudio_Update();
  722.  
  723.     if (host_speeds.value)
  724.     {
  725.         pass1 = (time1 - time3)*1000;
  726.         time3 = Sys_FloatTime ();
  727.         pass2 = (time2 - time1)*1000;
  728.         pass3 = (time3 - time2)*1000;
  729.         Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
  730.                     pass1+pass2+pass3, pass1, pass2, pass3);
  731.     }
  732.     
  733.     host_framecount++;
  734. }
  735.  
  736. void Host_Frame (float time)
  737. {
  738.     double    time1, time2;
  739.     static double    timetotal;
  740.     static int        timecount;
  741.     int        i, c, m;
  742.  
  743.     if (!serverprofile.value)
  744.     {
  745.         _Host_Frame (time);
  746.         return;
  747.     }
  748.     
  749.     time1 = Sys_FloatTime ();
  750.     _Host_Frame (time);
  751.     time2 = Sys_FloatTime ();    
  752.     
  753.     timetotal += time2 - time1;
  754.     timecount++;
  755.     
  756.     if (timecount < 1000)
  757.         return;
  758.  
  759.     m = timetotal*1000/timecount;
  760.     timecount = 0;
  761.     timetotal = 0;
  762.     c = 0;
  763.     for (i=0 ; i<svs.maxclients ; i++)
  764.     {
  765.         if (svs.clients[i].active)
  766.             c++;
  767.     }
  768.  
  769.     Con_Printf ("serverprofile: %2i clients %2i msec\n",  c,  m);
  770. }
  771.  
  772. //============================================================================
  773.  
  774.  
  775. extern int vcrFile;
  776. #define    VCR_SIGNATURE    0x56435231
  777. // "VCR1"
  778.  
  779. void Host_InitVCR (quakeparms_t *parms)
  780. {
  781.     int        i, len, n;
  782.     char    *p;
  783.     
  784.     if (COM_CheckParm("-playback"))
  785.     {
  786.         if (com_argc != 2)
  787.             Sys_Error("No other parameters allowed with -playback\n");
  788.  
  789.         Sys_FileOpenRead("quake.vcr", &vcrFile);
  790.         if (vcrFile == -1)
  791.             Sys_Error("playback file not found\n");
  792.  
  793.         Sys_FileRead (vcrFile, &i, sizeof(int));
  794.         if (i != VCR_SIGNATURE)
  795.             Sys_Error("Invalid signature in vcr file\n");
  796.  
  797.         Sys_FileRead (vcrFile, &com_argc, sizeof(int));
  798.         com_argv = malloc(com_argc * sizeof(char *));
  799.         com_argv[0] = parms->argv[0];
  800.         for (i = 0; i < com_argc; i++)
  801.         {
  802.             Sys_FileRead (vcrFile, &len, sizeof(int));
  803.             p = malloc(len);
  804.             Sys_FileRead (vcrFile, p, len);
  805.             com_argv[i+1] = p;
  806.         }
  807.         com_argc++; /* add one for arg[0] */
  808.         parms->argc = com_argc;
  809.         parms->argv = com_argv;
  810.     }
  811.  
  812.     if ( (n = COM_CheckParm("-record")) != 0)
  813.     {
  814.         vcrFile = Sys_FileOpenWrite("quake.vcr");
  815.  
  816.         i = VCR_SIGNATURE;
  817.         Sys_FileWrite(vcrFile, &i, sizeof(int));
  818.         i = com_argc - 1;
  819.         Sys_FileWrite(vcrFile, &i, sizeof(int));
  820.         for (i = 1; i < com_argc; i++)
  821.         {
  822.             if (i == n)
  823.             {
  824.                 len = 10;
  825.                 Sys_FileWrite(vcrFile, &len, sizeof(int));
  826.                 Sys_FileWrite(vcrFile, "-playback", len);
  827.                 continue;
  828.             }
  829.             len = Q_strlen(com_argv[i]) + 1;
  830.             Sys_FileWrite(vcrFile, &len, sizeof(int));
  831.             Sys_FileWrite(vcrFile, com_argv[i], len);
  832.         }
  833.     }
  834.     
  835. }
  836.  
  837. /*
  838. ====================
  839. Host_Init
  840. ====================
  841. */
  842. void Host_Init (quakeparms_t *parms)
  843. {
  844.     if (standard_quake)
  845.         minimum_memory = MINIMUM_MEMORY;
  846.     else
  847.         minimum_memory = MINIMUM_MEMORY_LEVELPAK;
  848.  
  849.     if (COM_CheckParm ("-minmemory"))
  850.         parms->memsize = minimum_memory;
  851.  
  852.     host_parms = *parms;
  853.  
  854.     if (parms->memsize < minimum_memory)
  855.         Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000);
  856.  
  857.     com_argc = parms->argc;
  858.     com_argv = parms->argv;
  859.  
  860.     Memory_Init (parms->membase, parms->memsize);
  861.     Cbuf_Init ();
  862.     Cmd_Init ();    
  863.     V_Init ();
  864.     Chase_Init ();
  865.     Host_InitVCR (parms);
  866.     COM_Init (parms->basedir);
  867.     Host_InitLocal ();
  868.     W_LoadWadFile ("gfx.wad");
  869.     Key_Init ();
  870.     Con_Init ();    
  871.     M_Init ();    
  872.     PR_Init ();
  873.     Mod_Init ();
  874.     NET_Init ();
  875.     SV_Init ();
  876.  
  877.     Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
  878.     Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
  879.     
  880.     R_InitTextures ();        // needed even for dedicated servers
  881.  
  882.     if (cls.state != ca_dedicated)
  883.     {
  884.         host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
  885.         if (!host_basepal)
  886.             Sys_Error ("Couldn't load gfx/palette.lmp");
  887.         host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
  888.         if (!host_colormap)
  889.             Sys_Error ("Couldn't load gfx/colormap.lmp");
  890.  
  891. #ifndef _WIN32 // on non win32, mouse comes before video for security reasons
  892.         IN_Init ();
  893. #endif
  894.         VID_Init (host_basepal);
  895.  
  896.         Draw_Init ();
  897.         SCR_Init ();
  898.         R_Init ();
  899. #ifndef    _WIN32
  900.     // on Win32, sound initialization has to come before video initialization, so we
  901.     // can put up a popup if the sound hardware is in use
  902.         S_Init ();
  903. #else
  904.  
  905. #ifdef    GLQUAKE
  906.     // FIXME: doesn't use the new one-window approach yet
  907.         S_Init ();
  908. #endif
  909.  
  910. #endif    // _WIN32
  911.         CDAudio_Init ();
  912.         Sbar_Init ();
  913.         CL_Init ();
  914. #ifdef _WIN32 // on non win32, mouse comes before video for security reasons
  915.         IN_Init ();
  916. #endif
  917.     }
  918.  
  919.     Cbuf_InsertText ("exec quake.rc\n");
  920.  
  921.     Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
  922.     host_hunklevel = Hunk_LowMark ();
  923.  
  924.     host_initialized = true;
  925.     
  926.     Sys_Printf ("========Quake Initialized=========\n");    
  927. }
  928.  
  929.  
  930. /*
  931. ===============
  932. Host_Shutdown
  933.  
  934. FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
  935. to run quit through here before the final handoff to the sys code.
  936. ===============
  937. */
  938. void Host_Shutdown(void)
  939. {
  940.     static qboolean isdown = false;
  941.     
  942.     if (isdown)
  943.     {
  944.         printf ("recursive shutdown\n");
  945.         return;
  946.     }
  947.     isdown = true;
  948.  
  949. // keep Con_Printf from trying to update the screen
  950.     scr_disabled_for_loading = true;
  951.  
  952.     Host_WriteConfiguration (); 
  953.  
  954.     CDAudio_Shutdown ();
  955.     NET_Shutdown ();
  956.     S_Shutdown();
  957.     IN_Shutdown ();
  958.  
  959.     if (cls.state != ca_dedicated)
  960.     {
  961.         VID_Shutdown();
  962.     }
  963. }
  964.  
  965.